/* vccd/relay.c 
 * 
 * This file is part of vccd. 
 * 
 * vccd is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * vccd is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with vccd. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>

#include <vcc/vcc.h>
#include <vcc/vccd.h>
#include <vcc/version.h>
#include <klist.h>


extern struct klist_node connections, logged_users;

int relay_new_msg(int fd, void *t) {
	struct vcc_relay_header 	*req;
	char 				*msg;
	int 				len;

	req = t;
	len = ntohl(req->size) - sizeof(struct vcc_relay_header);

	if (unlikely(!(msg = amalloc(len)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return 1;
	}

	read_until(fd, msg, len);

	if (!*req->visible) 
		relay_broadcast(msg, fd, req);
	else 
		/* only send to somebody */
		relay_send_to(msg, req->visible, fd, req);

	afree(msg, len);

	return 0;
}


int relay_send_packet(int fd, const char *restrict buf, 
		const struct vcc_relay_header *restrict hdr, int size) {
	struct vcc_relay_header req;

	memcpy(&req, hdr, sizeof(struct vcc_relay_header));
	req.size = htonl(sizeof(struct vcc_relay_header) + size);

	write(fd, &req, sizeof(struct vcc_relay_header));
	write(fd, buf, size);

	return 0;
}


int send_to(int fd, const char *restrict msg, int is_only_visible, 
		const struct vcc_relay_header *restrict hdr, int size) {
	struct vcc_relay_header req;

	memset(&req, 0, sizeof(struct vcc_relay_header));

	req.magic = htonl(VCC_MAGIC_RL);
	req.reqtype = htonl(REQ_REL_NEW);
	req.uid = htonl(is_only_visible);
	req.session = 0;

	strncpy(req.usrname, hdr->usrname, USRNAME_SIZE);
	relay_send_packet(fd, msg, &req, size);

	return 0;
}

int relay_send_to(const char *restrict msg, const char *restrict visible, int expect_fd, 
		const struct vcc_relay_header *restrict hdr) {
	struct klist_node 		*n;
	struct user 			*u;

	vcc_write_all();

	for (n = logged_users.next; n != &logged_users; n = n->next) {
		u = n->data;

		if (n->value == expect_fd) 
			continue;

		if (!strcmp(visible, u->username)) {
			send_to(n->value, msg, 1, hdr, strlen(msg));
			vcc_log("Relay message sent to the visible user. \n");

			return 0;
		}
	}

	vcc_log("*** user not found. \n");

	return 1;
}


int relay_broadcast(const char *restrict msg, int expect_fd, const struct vcc_relay_header *restrict hdr) {
	struct klist_node 	*n;
	int 			i, len;

	/* make sure about that the write() bottom will execute after these */
	vcc_write_all();

	len = strlen(msg);
	i = 0;

	for (n = connections.next; n != &connections; n = n->next) {
		if (n->value == expect_fd) 
			continue;

		send_to(n->value, msg, 0, hdr, len);

		i++;
	}

	vcc_log("Relay message sent to %d users. \n", i);

	return 0;
}


